/******************************************************************************* * Copyright (c) 2006, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.corext.refactoring.binary; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IAnnotatable; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMemberValuePair; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeParameter; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.internal.corext.refactoring.Checks; import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; public class StubCreator { /** The internal string buffer */ protected StringBuffer fBuffer; /** Should stubs for private member be generated as well? */ protected final boolean fStubInvisible; public StubCreator(final boolean stubInvisible) { fStubInvisible= stubInvisible; } protected void appendEnumConstants(final IType type) throws JavaModelException { final IField[] fields= type.getFields(); final List<IField> list= new ArrayList<IField>(fields.length); for (int index= 0; index < fields.length; index++) { final IField field= fields[index]; if (Flags.isEnum(field.getFlags())) list.add(field); } for (int index= 0; index < list.size(); index++) { if (index > 0) fBuffer.append(","); //$NON-NLS-1$ fBuffer.append(list.get(index).getElementName()); } fBuffer.append(";"); //$NON-NLS-1$ } protected void appendExpression(final String signature) { switch (signature.charAt(0)) { case Signature.C_BOOLEAN: fBuffer.append("false"); //$NON-NLS-1$ break; case Signature.C_BYTE: case Signature.C_CHAR: case Signature.C_DOUBLE: case Signature.C_FLOAT: case Signature.C_INT: case Signature.C_LONG: case Signature.C_SHORT: fBuffer.append("0"); //$NON-NLS-1$ break; default: fBuffer.append("("); //$NON-NLS-1$ fBuffer.append(Signature.toString(signature)); fBuffer.append(")"); //$NON-NLS-1$ fBuffer.append("null"); //$NON-NLS-1$ break; } } protected void appendFieldDeclaration(final IField field) throws JavaModelException { appendFlags(field); fBuffer.append(" "); //$NON-NLS-1$ final String signature= field.getTypeSignature(); fBuffer.append(Signature.toString(signature)); fBuffer.append(" "); //$NON-NLS-1$ fBuffer.append(field.getElementName()); if (Flags.isFinal(field.getFlags())) { fBuffer.append("="); //$NON-NLS-1$ appendExpression(signature); } fBuffer.append(";"); //$NON-NLS-1$ } protected void appendFlags(final IMember member) throws JavaModelException { if (member instanceof IAnnotatable) for (IAnnotation annotation : ((IAnnotatable) member).getAnnotations()) { appendAnnotation(annotation); } int flags= member.getFlags(); final int kind= member.getElementType(); if (kind == IJavaElement.TYPE) { flags&= ~Flags.AccSuper; final IType type= (IType) member; if (!type.isMember()) flags&= ~Flags.AccPrivate; } if (Flags.isEnum(flags)) flags&= ~Flags.AccFinal; if (kind == IJavaElement.METHOD) { flags&= ~Flags.AccVarargs; flags&= ~Flags.AccBridge; } if (flags != 0) fBuffer.append(Flags.toString(flags)); } private void appendAnnotation(IAnnotation annotation) throws JavaModelException { fBuffer.append('@'); fBuffer.append(annotation.getElementName()); fBuffer.append('('); IMemberValuePair[] memberValuePairs= annotation.getMemberValuePairs(); for (IMemberValuePair pair : memberValuePairs) { fBuffer.append(pair.getMemberName()); fBuffer.append('='); appendAnnotationValue(pair.getValue(), pair.getValueKind()); fBuffer.append(','); } if (memberValuePairs.length > 0) fBuffer.deleteCharAt(fBuffer.length() - 1); fBuffer.append(')').append('\n'); } private void appendAnnotationValue(Object value, int valueKind) throws JavaModelException { if (value instanceof Object[]) { Object[] objects= (Object[]) value; fBuffer.append('{'); for (Object object : objects) { appendAnnotationValue(object, valueKind); fBuffer.append(','); } if (objects.length > 0) fBuffer.deleteCharAt(fBuffer.length() - 1); fBuffer.append('}'); } else { switch (valueKind) { case IMemberValuePair.K_ANNOTATION: appendAnnotation((IAnnotation) value); break; case IMemberValuePair.K_STRING: fBuffer.append('"').append(value).append('"'); break; default: fBuffer.append(value); break; } } } protected void appendMembers(final IType type, final IProgressMonitor monitor) throws JavaModelException { try { monitor.beginTask(RefactoringCoreMessages.StubCreationOperation_creating_type_stubs, 1); final IJavaElement[] children= type.getChildren(); for (int index= 0; index < children.length; index++) { final IMember child= (IMember) children[index]; final int flags= child.getFlags(); final boolean isPrivate= Flags.isPrivate(flags); final boolean isDefault= !Flags.isPublic(flags) && !Flags.isProtected(flags) && !isPrivate; final boolean stub= fStubInvisible || (!isPrivate && !isDefault); if (child instanceof IType) { if (stub) appendTypeDeclaration((IType) child, new SubProgressMonitor(monitor, 1)); } else if (child instanceof IField) { if (stub && !Flags.isEnum(flags) && !Flags.isSynthetic(flags)) appendFieldDeclaration((IField) child); } else if (child instanceof IMethod) { final IMethod method= (IMethod) child; final String name= method.getElementName(); if (method.getDeclaringType().isEnum()) { final int count= method.getNumberOfParameters(); if (count == 0 && "values".equals(name)) //$NON-NLS-1$ continue; if (count == 1 && "valueOf".equals(name) && "Ljava.lang.String;".equals(method.getParameterTypes()[0])) //$NON-NLS-1$ //$NON-NLS-2$ continue; if (method.isConstructor()) continue; } boolean skip= !stub || name.equals("<clinit>"); //$NON-NLS-1$ if (method.isConstructor()) skip= false; skip= skip || Flags.isSynthetic(flags) || Flags.isBridge(flags); if (!skip) appendMethodDeclaration(method); } fBuffer.append("\n"); //$NON-NLS-1$ } } finally { monitor.done(); } } protected void appendMethodBody(final IMethod method) throws JavaModelException { if (method.isConstructor()) { final IType declaringType= method.getDeclaringType(); String superSignature= declaringType.getSuperclassTypeSignature(); if (superSignature != null) { superSignature= Signature.getTypeErasure(superSignature); final IType superclass= declaringType.getJavaProject().findType(Signature.getSignatureQualifier(superSignature), Signature.getSignatureSimpleName(superSignature)); if (superclass != null) { final IMethod[] superMethods= superclass.getMethods(); IMethod superConstructor= null; final int length= superMethods.length; for (int index= 0; index < length; index++) { final IMethod superMethod= superMethods[index]; if (superMethod.isConstructor() && !Flags.isPrivate(superMethod.getFlags())) { superConstructor= superMethod; if (superConstructor.getExceptionTypes().length == 0) break; } } if (superConstructor != null) { final String[] superParameters= superConstructor.getParameterTypes(); final int paramLength= superParameters.length; if (paramLength != 0) { fBuffer.append("super("); //$NON-NLS-1$ for (int index= 0; index < paramLength; index++) { if (index > 0) fBuffer.append(","); //$NON-NLS-1$ appendExpression(superParameters[index]); } fBuffer.append(");"); //$NON-NLS-1$ } } } } } else { String returnType= method.getReturnType(); if (!Signature.SIG_VOID.equals(returnType)) { fBuffer.append("return "); //$NON-NLS-1$ appendExpression(returnType); fBuffer.append(";"); //$NON-NLS-1$ } } } protected void appendMethodDeclaration(final IMethod method) throws JavaModelException { appendFlags(method); fBuffer.append(" "); //$NON-NLS-1$ final ITypeParameter[] parameters= method.getTypeParameters(); if (parameters.length > 0) { appendTypeParameters(parameters); fBuffer.append(" "); //$NON-NLS-1$ } final String returnType= method.getReturnType(); if (!method.isConstructor()) { fBuffer.append(Signature.toString(returnType)); fBuffer.append(" "); //$NON-NLS-1$ } fBuffer.append(method.getElementName()); fBuffer.append("("); //$NON-NLS-1$ final String[] parameterTypes= method.getParameterTypes(); final int flags= method.getFlags(); final boolean varargs= Flags.isVarargs(flags); final int parameterLength= parameterTypes.length; for (int index= 0; index < parameterLength; index++) { if (index > 0) fBuffer.append(","); //$NON-NLS-1$ fBuffer.append(Signature.toString(parameterTypes[index])); if (varargs && index == parameterLength - 1) { final int length= fBuffer.length(); if (length >= 2 && fBuffer.indexOf("[]", length - 2) >= 0) //$NON-NLS-1$ fBuffer.setLength(length - 2); fBuffer.append("..."); //$NON-NLS-1$ } fBuffer.append(" "); //$NON-NLS-1$ appendMethodParameterName(method, index); } fBuffer.append(")"); //$NON-NLS-1$ final String[] exceptionTypes= method.getExceptionTypes(); final int exceptionLength= exceptionTypes.length; if (exceptionLength > 0) fBuffer.append(" throws "); //$NON-NLS-1$ for (int index= 0; index < exceptionLength; index++) { if (index > 0) fBuffer.append(","); //$NON-NLS-1$ fBuffer.append(Signature.toString(exceptionTypes[index])); } if (Flags.isAbstract(flags) || Flags.isNative(flags)) fBuffer.append(";"); //$NON-NLS-1$ else { fBuffer.append("{\n"); //$NON-NLS-1$ appendMethodBody(method); fBuffer.append("}"); //$NON-NLS-1$ } } /** * Appends a parameter name * * @param method the method * @param index the index of the parameter */ protected void appendMethodParameterName(IMethod method, int index) { fBuffer.append("a"); //$NON-NLS-1$ fBuffer.append(index); } protected void appendSuperInterfaceTypes(final IType type) throws JavaModelException { final String[] signatures= type.getSuperInterfaceTypeSignatures(); if (signatures.length > 0) { if (type.isInterface()) fBuffer.append(" extends "); //$NON-NLS-1$ else fBuffer.append(" implements "); //$NON-NLS-1$ } for (int index= 0; index < signatures.length; index++) { if (index > 0) fBuffer.append(","); //$NON-NLS-1$ fBuffer.append(Signature.toString(signatures[index])); } } protected void appendTopLevelType(final IType type, IProgressMonitor subProgressMonitor) throws JavaModelException { String packageName= type.getPackageFragment().getElementName(); if (packageName.length() > 0) { fBuffer.append("package "); //$NON-NLS-1$ fBuffer.append(packageName); fBuffer.append(";\n"); //$NON-NLS-1$ } appendTypeDeclaration(type, subProgressMonitor); } protected void appendTypeDeclaration(final IType type, final IProgressMonitor monitor) throws JavaModelException { try { monitor.beginTask(RefactoringCoreMessages.StubCreationOperation_creating_type_stubs, 1); if (type.isAnnotation()) { appendFlags(type); fBuffer.append(" @interface "); //$NON-NLS-1$ fBuffer.append(type.getElementName()); fBuffer.append("{\n"); //$NON-NLS-1$ appendMembers(type, new SubProgressMonitor(monitor, 1)); fBuffer.append("}"); //$NON-NLS-1$ } else if (type.isInterface()) { appendFlags(type); fBuffer.append(" interface "); //$NON-NLS-1$ fBuffer.append(type.getElementName()); appendTypeParameters(type.getTypeParameters()); appendSuperInterfaceTypes(type); fBuffer.append("{\n"); //$NON-NLS-1$ appendMembers(type, new SubProgressMonitor(monitor, 1)); fBuffer.append("}"); //$NON-NLS-1$ } else if (type.isClass()) { appendFlags(type); fBuffer.append(" class "); //$NON-NLS-1$ fBuffer.append(type.getElementName()); appendTypeParameters(type.getTypeParameters()); final String signature= type.getSuperclassTypeSignature(); if (signature != null) { fBuffer.append(" extends "); //$NON-NLS-1$ fBuffer.append(Signature.toString(signature)); } appendSuperInterfaceTypes(type); fBuffer.append("{\n"); //$NON-NLS-1$ appendMembers(type, new SubProgressMonitor(monitor, 1)); fBuffer.append("}"); //$NON-NLS-1$ } else if (type.isEnum()) { appendFlags(type); fBuffer.append(" enum "); //$NON-NLS-1$ fBuffer.append(type.getElementName()); appendSuperInterfaceTypes(type); fBuffer.append("{\n"); //$NON-NLS-1$ appendEnumConstants(type); appendMembers(type, new SubProgressMonitor(monitor, 1)); fBuffer.append("}"); //$NON-NLS-1$ } } finally { monitor.done(); } } protected void appendTypeParameters(final ITypeParameter[] parameters) throws JavaModelException { final int length= parameters.length; if (length > 0) fBuffer.append("<"); //$NON-NLS-1$ for (int index= 0; index < length; index++) { if (index > 0) fBuffer.append(","); //$NON-NLS-1$ final ITypeParameter parameter= parameters[index]; fBuffer.append(parameter.getElementName()); final String[] bounds= parameter.getBounds(); final int size= bounds.length; if (size > 0) fBuffer.append(" extends "); //$NON-NLS-1$ for (int offset= 0; offset < size; offset++) { if (offset > 0) fBuffer.append(" & "); //$NON-NLS-1$ fBuffer.append(bounds[offset]); } } if (length > 0) fBuffer.append(">"); //$NON-NLS-1$ } /** * Creates and returns a stub for the given top-level type. * * @param topLevelType the top-level type * @param monitor the progress monitor, can be <code>null</code> * @return the source stub * @throws JavaModelException if this element does not exist or if an exception occurs while * accessing its corresponding resource */ public String createStub(IType topLevelType, IProgressMonitor monitor) throws JavaModelException { Assert.isTrue(Checks.isTopLevel(topLevelType)); if (monitor == null) monitor= new NullProgressMonitor(); fBuffer= new StringBuffer(2046); appendTopLevelType(topLevelType, monitor); String result= fBuffer.toString(); fBuffer= null; return result; } }